
#include <cstring>
#include <chrono>
#include <thread>
#include <cstdlib>
#include <cerrno>
#include <string>
#include "securec.h"
#include <iostream>
#include "dmcApi.h"
#include "handleStr.h"

int deviceId = 1;

enum DlnaUpnpMediaType {
    INVALID = -1,
    AUDIO,
    VIDEO,
    IMAGE,
    FOLDER,
};

struct MediaInfo {
    std::string mediaId{ "" };
    std::string mediaName{ "" };
    std::string mediaUrl{ "" };
    std::string mediaType{ "" };
    size_t mediaSize{ 0 };
    uint32_t startPosition{ 0 };
    uint32_t duration{ 0 };
    uint32_t closingCreditsPosition{ 0 };
    std::string albumCoverUrl{ "" };
    std::string albumTitle{ "" };
    std::string mediaArtist{ "" };
    std::string lrcUrl{ "" };
    std::string lrcContent{ "" };
    std::string appIconUrl{ "" };
    std::string appName{ "" };
    std::string drmType;
};

std::string DlnaGetUpnpClass(DlnaUpnpMediaType mediaInfoType)
{
    // 根据元媒体类型或是媒体信息类型构建UpnpClass
    std::string upnpClass;
    switch (mediaInfoType) {
        case AUDIO:
            upnpClass = "object.item.audioItem.musicTrack";
            break;
        case VIDEO:
            upnpClass = "object.item.videoItem";
            break;
        case IMAGE:
            upnpClass = "object.item.imageItem.photo";
            break;
        case FOLDER:
            upnpClass = "object.container";
            break;
        default:
            break;
    }

    return upnpClass;
}
std::string DlnaGetMetaData(DlnaUpnpMediaType mediaInfoType, std::string url,
    const MediaInfo &mediaInfo)
{
    std::string title = mediaInfo.mediaName.empty() ? "media" : mediaInfo.mediaName;
    std::string artist = mediaInfo.mediaArtist.empty() ? "anonymous" : mediaInfo.mediaArtist;
    std::string size = "0";
    std::string protocolInfo = "http-get:*:*:*";
    std::string end = "</DIDL-Lite>";
    std::string sb;
    sb += "<DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" ";
    sb += "xmlns:dlna=\"urn:schemas-dlna-org:metadata-1-0/\" ";
    sb +=  "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" ";
    sb +=  "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">";
    sb.append("<item id=\"0\" parentID=\"0\" restricted=\"1\"><dc:title>");
    sb.append(title);
    sb.append("</dc:title><upnp:artist>");
    sb.append(artist);
    sb.append("</upnp:artist><upnp:class>");
    sb.append(DlnaGetUpnpClass(mediaInfoType));
    sb.append("</upnp:class><res size=\"");
    sb.append(size);
    sb.append("\" protocolInfo=\"");
    sb.append(protocolInfo);
    sb.append("\">");
    sb.append(DlnaDmc::ReplaceUrl(url));

    sb.append("</res></item>");
    sb.append(end);

    return sb;
}

DlnaDmc::dmc_int32 TestPlay(DlnaDmc::DmcApi &dmcApiObj){
     printf("testPlay begin.\n");
     MediaInfo mediaInfo{
         .mediaId = "bigbuck_001",  // 唯一视频标识
         .mediaName = "Big Buck Bunny测试视频",
         .mediaUrl = "https://media.w3.org/2010/05/sintel/trailer.mp4",
         .mediaType = "video/mp4",  // MP4的MIME类型
         .mediaSize = 0,            // 流媒体无需指定大小
         .startPosition = 0,        // 从头播放
         .duration = 60000,         // 视频时长60秒
         .closingCreditsPosition = 55000,  //片尾开始时间 
         .albumCoverUrl = "https://media.w3.org/2010/05/sintel/poster.png",
         .albumTitle = "W3C标准测试视频集",
         .mediaArtist = "Blender Foundation",  // 实际制作方
         .lrcUrl = "",              // 无歌词文件
         .lrcContent = "",          // 无歌词内容
         .appIconUrl = "",
         .appName = "智能播放器Demo",
         .drmType = ""              // 无DRM保护
     };

    DlnaUpnpMediaType mediaType=VIDEO;
    std::string url = mediaInfo.mediaUrl;
    std::string metaData = DlnaGetMetaData(mediaType, url, mediaInfo);
    printf("SetAvTransportURI begins.");
    auto ret = dmcApiObj.SetAvTransportURI(deviceId, 0, url, metaData);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("SetAvTransportURI failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("SetAvTransportURI ends.");

    printf("Play begins.");
    std::string playSpeedDetail;
    ret = dmcApiObj.Play(deviceId, 0, playSpeedDetail);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("Play failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("Play ends.");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestPause(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestPause begin.\n");
    auto ret = dmcApiObj.Pause(deviceId, 0);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("Pause failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("Pause ends.");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestStop(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestStop begin.\n");
    auto ret = dmcApiObj.Stop(deviceId, 0);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("Stop failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("Stop ends.");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestSeek(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestSeek begin.\n");
    auto ret = dmcApiObj.Seek(deviceId, 0, DlnaDmc::DMC_SEEK_MODE_REL_TIME, "00:00:30");
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("TestSeek failed.\n");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("TestSeek ends.");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestSetVolume(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestStop begin.\n");
    auto ret = dmcApiObj.SetVolume(deviceId, 0, DlnaDmc::DMC_CHANNEL_TYPE_MASTER, 100);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("SetVolume failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("SetVolume ends.");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestGetVolume(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestGetVolume begin.\n");
    DlnaDmc::dmc_uint32 currentvolume;
    auto ret = dmcApiObj.GetVolume(deviceId, 0, DlnaDmc::DMC_CHANNEL_TYPE_MASTER, currentvolume);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("GetVolume failed.\n");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("TestGetVolume ends, currentvolume=%u\n.", currentvolume);
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestSetMute(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestSetMute begin.\n");
    auto ret = dmcApiObj.SetMute(deviceId, 0, DlnaDmc::DMC_CHANNEL_TYPE_MASTER, true);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("SetMute true failed.\n");
        return DlnaDmc::DMC_FAILURE;
    }

    std::this_thread::sleep_for(std::chrono::seconds(10));
    ret = dmcApiObj.SetMute(deviceId, 0, DlnaDmc::DMC_CHANNEL_TYPE_MASTER, false);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("SetMute false failed.\n");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("TestSetMute ends.");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestPlaySpeed(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestPlaySpeed begin.\n");
    std::string playSpeedDetail = "2";
    auto ret = dmcApiObj.Play(deviceId, 0, playSpeedDetail);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("TestPlaySpeed failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("TestPlaySpeed ends.\n");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestGetPositionInfo(DlnaDmc::DmcApi &dmcApiObj){
    printf("TestGetPositionInfo begin.\n");
    DlnaDmc::DmrTrackPosition trackPos;
    auto ret = dmcApiObj.GetPositionInfo(deviceId, 0, trackPos);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("TestGetPositionInfo failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("currentTrack: %u\n", trackPos.currentTrack);
    printf("trackDur: %s\n", trackPos.trackDur.c_str());
    printf("trackMetaData: %s\n", trackPos.trackMetaData.c_str());
    printf("trackUri: %s\n", trackPos.trackUri.c_str());
    printf("relTime: %s\n", trackPos.relTime.c_str());
    printf("absTime: %s\n", trackPos.absTime.c_str());
    printf("relCount: %d\n", trackPos.relCount);
    printf("absCount: %d\n", trackPos.absCount);
    printf("TestGetPositionInfo ends.\n");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 TestGetTransportInfo(DlnaDmc::DmcApi &dmcApiObj)
{
    printf("TestGetTransportInfo begin.\n");
    DlnaDmc::DmcTransportState transportState;
    DlnaDmc::DmcTransportStatus transportStatus;
    std::string speed;
    auto ret = dmcApiObj.GetTransportInfo(deviceId, 0, transportState, transportStatus, speed);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        printf("GetTransportInfo failed.");
        return DlnaDmc::DMC_FAILURE;
    }
    printf("transportState: %d\n", transportState);
    printf("transportStatus: %d\n", transportStatus);
    printf("speed: %s\n", speed.c_str());
    printf("TestGetTransportInfo ends.\n");
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 DeviceUp(const DlnaDmc::UploadDeviceInfo &deviceInfo)
{
    printf("Device up id = %u.\n", deviceInfo.deviceId);
    printf("friendlyName = %s.\n", deviceInfo.friendlyName.c_str());
    return DlnaDmc::DMC_SUCCESS;
}

DlnaDmc::dmc_int32 DeviceDown(DlnaDmc::dmc_uint32 deviceId)
{
    printf("Device down id = %u\n.", deviceId);
    return DlnaDmc::DMC_SUCCESS;
}

int ParsePara(int argc, char* argv[], int &instanceId)
{
    if (argc != 2) {
        std::cout << "usage: " << argv[0] << " <int>" << std::endl;
        return 1;
    }
    char* endptr;
    errno = 0;
    long number = std::strtol(argv[1], &endptr, 10);
    if (*endptr != '\0') {
        std::cout << "exist no-digital char" << std::endl;
        return 1;
    }
    
    if ((errno == ERANGE && (number == LONG_MAX || number == LONG_MIN)) ||
        (number > INT_MAX) || (number < INT_MIN)) {
        std::cout << "out of range" << std::endl;
        return 1;
    }

    deviceId = static_cast<int>(number);
    std::cout << "deviceId passed: " << deviceId << std::endl;
    return 0;
}

int main(int argc, char* argv[]) {
    if(ParsePara(argc, argv, deviceId) != 0) {
        std::cout << "Parse Para error" << std::endl;
        return 1;
    }

    DlnaDmc::DmcApi dmcApiObj = DlnaDmc::DmcApi();

    DlnaDmc::DmcCbType cb;
    cb.deviceUpCb = DeviceUp;
    cb.deviceDownCb = DeviceDown;
    DlnaDmc::dmc_int32 result = dmcApiObj.InitEnv("wlan0", cb);
    if (result != DlnaDmc::DMC_SUCCESS) {
        printf("init env failed.");
        return -1;
    }
    printf("InitEnv success\n");
    std::this_thread::sleep_for(std::chrono::seconds(10));

    auto ret = TestPlay(dmcApiObj);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        return -1;
    }

    std::this_thread::sleep_for(std::chrono::seconds(5));
    ret = TestGetTransportInfo (dmcApiObj);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        return -1;
    }

    TestPause(dmcApiObj);
    std::this_thread::sleep_for(std::chrono::seconds(2));
    ret = TestGetTransportInfo (dmcApiObj);
    if (ret != DlnaDmc::DMC_SUCCESS) {
        return -1;
    }

    std::this_thread::sleep_for(std::chrono::seconds(60 * 10));
    TestPause(dmcApiObj);
    TestSetMute(dmcApiObj);
    TestSetVolume(dmcApiObj);
    TestPlaySpeed(dmcApiObj);
    TestPlay(dmcApiObj);
    TestGetVolume(dmcApiObj);
    TestGetPositionInfo(dmcApiObj);
    TestStop(dmcApiObj);
    TestSeek(dmcApiObj);
    return 0;
}
